package AliDiepPR.ANN;

import java.io.*;

import AliDiepPR.ANN.Network;
import AliDiepPR.ANN.NeuralNetworkException;
import AliDiepPR.ANN.ActivationFunction.BipolarSigmoidFunction;
import AliDiepPR.ANN.ActivationFunction.HyperbolicTangensFunction;
import AliDiepPR.ANN.ActivationFunction.IActivationFunction;
import AliDiepPR.ANN.ActivationFunction.SigmoidFunction;

public class ReaderWriterNetwork {

	/**
	 * @param path
	 *            to file AliDiepPR.ANN
	 * @throws IOException
	 * @throws NeuralNetworkException
	 */
	public static Network ReadNetwork(InputStream is) throws IOException,
			NeuralNetworkException {

		if (is != null) {
			Writer writer = new StringWriter();

			char[] buffer = new char[1000000];
			try {
				Reader reader = new BufferedReader(new InputStreamReader(is,
						"UTF-8"));
				int n;
				while ((n = reader.read(buffer)) != -1) {
					writer.write(buffer, 0, n);
					if (n > 1000000)
						throw new IOException();
				}
			} finally {
				is.close();
			}
			String str = writer.toString();
			Network net = ReadNetworkFromString(str);
			return net;
		}
		return null;
	}

	public static Network ReadNetwork(String path)
			throws NeuralNetworkException {
		try {
			BufferedReader out = new BufferedReader(new FileReader(path));
			Network net = ReadNetworkFromStream(out);
			return net;
		} catch (Exception exc) {
			throw new NeuralNetworkException(exc.getMessage());
		}
	}

	public static Network ReadNetworkFromStream(BufferedReader out)
			throws NeuralNetworkException {
		try {

			int numLayer = Integer.parseInt(out.readLine());
			int[] info = new int[numLayer];

			String str = out.readLine();
			String[] split = str.split(" ");

			for (int i = 0; i < numLayer; i++)
				info[i] = Integer.parseInt(split[i]);

			double[][] threshold = new double[numLayer][];

			for (int i = 1; i < numLayer; i++) {
				threshold[i] = new double[info[i]];
				str = out.readLine();
				split = str.split(" ");
				for (int j = 0; j < info[i]; j++)
					threshold[i][j] = Double.parseDouble(split[j]);
			}

			double[][][] wei = new double[numLayer][][];

			for (int lay = 0; lay < numLayer - 1; lay++) {
				wei[lay] = new double[info[lay + 1]][];
				for (int j = 0; j < info[lay + 1]; j++) {
					wei[lay][j] = new double[info[lay]];
					str = out.readLine();
					split = str.split(" ");
					for (int i = 0; i < info[lay]; i++)
						wei[lay][j][i] = Double.parseDouble(split[i]);
				}
			}

			Network net = new Network();
			net.InitNetwork(numLayer, info, threshold, wei);

			return net;
		} catch (Exception exc) {
			throw new NeuralNetworkException(exc.getMessage());
		}
	}

	public static Network ReadNetworkFromString(String str)
			throws NeuralNetworkException {
		try {
			int off = 0;
			String config[] = str.split("\\r?\\n");
			int numLayer = Integer.parseInt(config[off]);
			int[] info = new int[numLayer];

			off++;
			String[] split = config[off].split(" ");
			for (int i = 0; i < numLayer; i++)
				info[i] = Integer.parseInt(split[i]);

			off++;
			split = config[off].split(" ");
			double alfa = Double.parseDouble(split[0]);
			IActivationFunction func;
			if ("Sigmoid" == split[1])
				func = new SigmoidFunction(alfa);
			else if ("Bipolar" == split[1])
				func = new BipolarSigmoidFunction(alfa);
			else if ("Hyperbolic" == split[1])
				func = new HyperbolicTangensFunction(alfa);
			else
				func = new SigmoidFunction(alfa);

			off++;
			double[][] threshold = new double[numLayer][];
			for (int i = 1; i < numLayer; i++) {
				threshold[i] = new double[info[i]];
				str = config[off];
				split = str.split(" ");
				for (int j = 0; j < info[i]; j++)
					threshold[i][j] = Double.parseDouble(split[j]);
				off++;
			}

			double[][][] wei = new double[numLayer][][];

			for (int lay = 0; lay < numLayer - 1; lay++) {
				wei[lay] = new double[info[lay + 1]][];
				for (int j = 0; j < info[lay + 1]; j++) {
					wei[lay][j] = new double[info[lay]];
					str = config[off];
					split = str.split(" ");
					for (int i = 0; i < info[lay]; i++)
						wei[lay][j][i] = Double.parseDouble(split[i]);
					off++;
				}
			}

			Network net = new Network();
			net.InitNetwork(numLayer, info, threshold, wei);

			return net;
		} catch (Exception exc) {
			throw new NeuralNetworkException(exc.getMessage());
		}
	}

	/**
	 * @param net
	 *            Network
	 * @param path
	 *            to place save AliDiepPR.ANN
	 * @throws NeuralNetworkException
	 */
	public static void WriteNetwork(Network net, String path)
			throws NeuralNetworkException {
		try {
			PrintWriter out = new PrintWriter(new BufferedWriter(
					new FileWriter(path)));

			out.write(net.numLayer + "\n");

			for (int i = 0; i < net.numLayer; i++)
				out.write(net.layer[i].numUnit + " ");

			out.write("\n");

			for (int lay = 1; lay < net.numLayer; lay++) {
				for (int i = 0; i < net.layer[lay].numUnit; i++)
					out.write(net.layer[lay].unit[i].threshold + " ");
				out.write("\n");
			}

			for (int lay = 0; lay < net.numLayer - 1; lay++) {
				double[][] wei = net.layer[lay].PrintWeight(lay);
				for (int i = 0; i < wei.length; i++) {
					for (int j = 0; j < wei[0].length; j++)
						out.write(wei[i][j] + " ");
					out.write("\n");
				}
			}

			out.close();
		} catch (Exception exc) {
			throw new NeuralNetworkException(
					"Can't save network, something wrongs!");
		}
	}
}
